<template>
  <el-table :data="tableData" border stripe v-loading="loading" element-loading-text="加载中..." :element-loading-svg="svg" class="custom-loading-svg" element-loading-svg-view-box="-10, -10, 50, 50">
    <!-- 左侧序号 -->
    <el-table-column type="index" width="60" label="序号" align="center" />
    <template v-for="(col, index) in options">
      <el-table-column :label="col.label" :width="col.width" :align="col.align" :prop="col.prop">
        <template #default="scope">
          <!-- 编辑单元格, 显示编辑框 -->
          <div v-if="cellEditID === scope.$index + scope.column.id" class="editIcon">
            <el-input v-model="scope.row[col.prop]" size="small" clearable />
            <!-- 恢复显示编辑按钮 -->
            <div @click="cellEditID = ''" class="editIcon">
              <el-icon-check @click="check(scope)" style="color: green;" />
              <el-icon-close @click="close(scope)" style="color: orangered;" />
            </div>
          </div>
          <div v-else-if="rowEditIndex === scope.$index">
            <el-input v-model="scope.row[col.prop]" size="small" clearable class="editIcon" />
          </div>
          <div v-else>
            <!-- 操作项列 -->
            <slot v-if="col.action" name="action" :scope="scope" />
            <!-- 带插槽的数据 -->
            <slot v-if="col.useSlot" :name="col.prop" :scope="scope"></slot>
            <!-- 显示数据,如果上面绑定了 prop 此行可不加,
               但是为了能定制显示内容,移除了绑定 -->
            <span v-else>
              {{ scope.row[col.prop] }}
            </span>
            <!-- 编辑图标 -->
            <el-icon-edit v-if="col.editable" @click="clickEdit(scope)" />
          </div>
        </template>
      </el-table-column>
    </template>
    <!-- 行编辑功能 -->
    <el-table-column label="行操作" width="136px">
      <template #default="scope">
        <div v-if="rowEditIndex === scope.$index">
          <el-button type="success" size="small" @click="confirm(scope)">确认</el-button>
          <el-button type="danger" size="small" @click="reset(scope)">重置</el-button>
        </div>
        <div v-else-if="isRowEditable">
          <el-button type="success" size="small" @click="rowEdit(scope)">编辑</el-button>
          <el-button type="danger" size="small" @click="rowDel(scope)">删除</el-button>
        </div>
      </template>
    </el-table-column>
  </el-table>
  <!-- 分页显示 -->
  <el-pagination
    class="pagination"
    small
    v-if="pagination"
    @size-change="sizeChange"
    @current-change="currentChange"
    v-model:currentPage="currentPage"
    :page-sizes="pageSizes"
    :page-size="curPageSize"
    layout="total, sizes, prev, pager, next, jumper"
    :total="total"
    background
  />
</template>

<script setup lang="ts">
import { cloneDeep } from 'lodash'
import { PropType, computed, ref, watch, onMounted } from 'vue'
import { TableOptions } from './types'

// ------------ 定义属性 ------------
let props = defineProps({
  // 表格配置
  options: {
    type: Array as PropType<TableOptions[]>,
    required: true,
  },
  // 表格数据
  data: {
    type: Array as PropType<any[]>,
    required: true,
  },
  // 是否启用行编辑功能
  isRowEditable: {
    type: Boolean,
    default: false,
  },
  pagination: {
    type: Boolean,
    default: false,
  },
  // 分页选项
  pageSizes: {
    type: Array as PropType<number[]>,
    default: [10, 30, 50, 100],
  },
  // 每页项数
  pageSize: {
    type: Number,
    default: 10,
  },
  // 当前页
  // currentPage: {
  //   type: Number,
  //   default: 1,
  // },
  // 总页数
  total: {
    type: Number,
    default: 100,
  },
})

// ------------ 显示数据加载过程 ------------
let loading = computed(() => !props.data || props.data.length == 0)
const svg = `
        <path class="path" d="
          M 30 15
          L 28 17
          M 25.61 25.61
          A 15 15, 0, 0, 1, 15 30
          A 15 15, 0, 1, 1, 27.99 7.5
          L 15 15
        " style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
      `
// ------------ 单元格内容编辑 ------------
let emits = defineEmits(['cellChanged', 'rowChanged', 'rowDeleted', 'pageSizeChanged', 'currentPageChanged', 'update:pageSize'])
// $index 行序号(0开始)  column.id 列标识
let cellEditID = ref('') // 被编辑的单元格 id
let cellEditOld = ref() // 被编辑单元格的原值
let clickEdit = (scope: any) => {
  cellEditID.value = scope.$index + scope.column.id
  cellEditOld.value = scope.row[scope.column.property]
}
// 编辑单元格的事件:由于 v-model 绑定，值已经被修改
let check = (scope: any) => {
  if (scope.row[scope.column.property] !== cellEditOld.value) {
    emits('cellChanged', scope)
  }
}
// 点击 叉叉, 恢复为原值
let close = (scope: any) => {
  scope.row[scope.column.property] = cellEditOld.value
  cellEditOld.value = ''
}

// ------------ 行编辑 ------------
// 复制数据(不能直接编辑父组件数据)
let tableData = ref<any[]>(cloneDeep(props.data))
// 数据变化
watch(
  () => props.data,
  val => {
    tableData.value = val
  },
  { deep: true },
)

// 当前正在编辑行的id
let rowEditIndex = ref(-1)
// 行编辑事件
let rowEdit = (scope: any) => {
  rowEditIndex.value = scope.$index // 指定编辑行
}
let rowDel = (scope: any) => {
  tableData.value.splice(scope.$index, 1)
  props.data.splice(scope.$index, 1)
  emits('rowDeleted', scope) // 通知父组件行删除了
}
let confirm = (scope: any) => {
  rowEditIndex.value = scope.$index // 指定编辑行
  rowEditIndex.value = -1
  emits('rowChanged', scope) // 通知父组件行变化了
}
// 重置行内容
let reset = (scope: any) => {
  // 用原始 props.data 内容恢复
  console.log(tableData.value[scope.$index])
  console.log(props.data[scope.$index])

  tableData.value[scope.$index] = cloneDeep(props.data[scope.$index])
  rowEditIndex.value = -1
}

// 分页功能: 每页数量变化
let curPageSize = ref(0)
// 父组件设置 pageSize 生效
onMounted(() => {
  curPageSize.value = props.pageSize
})
let sizeChange = (val: number) => {
  curPageSize.value = val
  emits('update:pageSize', val)
  emits('pageSizeChanged', val)
}
// 当前页变化
let currentPage = ref(1)
let currentChange = (val: number) => {
  currentPage.value = val
  emits('currentPageChanged', val)
}
</script>

<style scoped lang="scss">
// 单元格中的编辑按钮
svg {
  height: 1em;
  width: 1em;
  cursor: pointer;
  margin-left: 4px;
}

.editIcon {
  display: flex;
  // 编辑单元格的样式按钮中的对勾与叉叉
  svg {
    height: 1.2em;
    width: 1.2em;
    margin-top: 0.2em;
  }
}

// 分页显示
.pagination {
  display: flex;
  margin-top: 4px;
}
</style>
